document.addEventListener("mousedown", function (event) {
  let dragElement = event.target.closest(".draggable");

  if (!dragElement) return;

  event.preventDefault();
  let shiftX, shiftY;

  startDrag(event.clientX, event.clientY);

  document.addEventListener("mousemove", onMouseMove);
  dragElement.onmouseup = function () {
    finishDrag();
  };

  function startDrag(clientX, clientY) {
    shiftX = clientX - dragElement.getBoundingClientRect().left;
    shiftY = clientY - dragElement.getBoundingClientRect().top;

    dragElement.style.position = "fixed";
    document.body.append(dragElement);

    moveAt(clientX, clientY);
  }

  function finishDrag() {
    dragElement.style.top =
      parseInt(dragElement.style.top) + window.scrollY + "px";
    dragElement.style.position = "absolute";

    document.removeEventListener("mousemove", onMouseMove);
    dragElement.onmouseup = null;
  }

  function onMouseMove(event) {
    moveAt(event.clientX, event.clientY);
  }

  function moveAt(clientX, clientY) {
    let newLeft = clientX - shiftX;
    let newTop = clientY - shiftY;

    let newBottom = newTop + dragElement.offsetHeight;

    if (newBottom > document.documentElement.clientHeight) {
      let docBottom = document.documentElement.getBoundingClientRect().bottom;
      let scrollY = Math.min(docBottom - newBottom, 10);
      if (scrollY < 0) scrollY = 0;

      window.scrollBy(0, scrollY);

      newTop = Math.min(
        newTop,
        document.documentElement.clientHeight - dragElement.offsetHeight
      );
    }

    if (newTop < 0) {
      let scrollY = Math.min(newTop, 10);
      if (scrollY < 0) scrollY = 0;

      window.scrollBy(0, -scrollY);
      newTop = Math.max(newTop, 0);
    }

    if (newLeft < 0) newLeft = 0;
    if (
      newLeft >
      document.documentElement.clientWidth - dragElement.offsetWidth
    ) {
      newLeft = document.documentElement.clientWidth - dragElement.offsetWidth;
    }

    dragElement.style.left = newLeft + "px";
    dragElement.style.top = newTop + "px";
  }
});
